#include "lua_function.h"

#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include <tchdb.h>

#include "log.h"

#include "limit_resources_manager.h"

#define LUA_CALL(L, IN, OUT) lua_call(L, IN, OUT)

//extern lua_State * main_L;
static resid_t tc_manager_id = INVALIDATE_ID;
static struct ResourcesOperation tc_opt;

struct tc {
	resid_t id;
	TCHDB * db_ptr;
};

static void * cb_tc_alloc(void * ctx)
{
	return malloc(sizeof(struct tc));
}

static int cb_tc_init(resid_t id, void * res, void * init_data, void * ctx)
{
	struct tc * db = (struct tc *)res;
	db->id = id;
	db->db_ptr = tchdbnew();
	int flag = HDBOWRITER|HDBOCREAT|HDBOLCKNB;
	if(!tchdbopen(db->db_ptr, (const char*)init_data, flag)) {
		WRITE_DEBUG_LOG("tchdbopen failed: %s", 
				tchdberrmsg(tchdbecode(db->db_ptr)));
		tchdbdel(db->db_ptr);
		db->db_ptr = 0;
		return -1;
	}
	return 0;
}

static void cb_tc_destory(resid_t id, void * res, void * ctx)
{
	struct tc * db = (struct tc *)res;
	db->id = INVALIDATE_ID;
	if (db->db_ptr) {
		tchdbclose(db->db_ptr);
		tchdbdel(db->db_ptr);
	}
	db->db_ptr = 0;
}

static void cb_tc_free(void * res, void * ctx)
{
	free(res);
}

struct tc * alloc_db(const char * file)
{
	if (tc_manager_id == INVALIDATE_ID) {
		LRMStartup(10);
		tc_opt.alloc = cb_tc_alloc;
		tc_opt.free = cb_tc_free;
		tc_opt.init = cb_tc_init;
		tc_opt.destory = cb_tc_destory;
		tc_manager_id = LRMCreate(1024, &tc_opt, 0);
		if (tc_manager_id == INVALIDATE_ID) {
			return 0;
		}
	}

	resid_t conn_id = LRMAllocResources(tc_manager_id, (void*)file);
	struct tc * db= (struct tc*)LRMGetResources(tc_manager_id, conn_id);
	return db;
}

void free_db(struct tc* db)
{
	LRMReleaseResources(tc_manager_id, db->id);
}

static int tc_open(lua_State * L)
{
	if(!lua_isstring(L, -1) ) {
		return 0;
	}

	const char * file = lua_tostring(L, -1);
	struct tc * db = alloc_db(file);
	if (db) {
		lua_pushinteger(L, db->id);
		return 1;
	}
	return 0;
}

static int tc_close(lua_State * L)
{
	if(!lua_isnumber(L, -1) ) {
		return 0;
	}

	int id = lua_tointeger(L, -1);
	struct tc * db = (struct tc *)LRMGetResources(tc_manager_id, id);
	if (db) {
		free_db(db);
		lua_pushinteger(L, 1);
		return 1;
	}
	return 0;
}

static int tc_put(lua_State * L)
{
	if(!lua_isnumber(L, -3) ) {
		return 0;
	}

	if(!lua_isstring(L, -2) ) {
		return 0;
	}

	if(!lua_isstring(L, -1) ) {
		return 0;
	}

	int id = lua_tointeger(L, -3);
	struct tc * db = (struct tc *)LRMGetResources(tc_manager_id, id);
	if (db == 0) {
		return 0;
	}
	
	size_t key_len = 0, val_len = 0;
	const char * key = lua_tolstring(L, -2, &key_len);
	const char * val = lua_tolstring(L, -1, &val_len);
	if(tchdbput(db->db_ptr, key, key_len, val, val_len)) {
		lua_pushinteger(L, 1);
		return 1;
	} else {
		return 0;
	}
}

static int tc_get(lua_State * L)
{
	if(!lua_isnumber(L, -2) ) {
		return 0;
	}

	if(!lua_isstring(L, -1) ) {
		return 0;
	}

	int id = lua_tointeger(L, -2);
	struct tc * db = (struct tc *)LRMGetResources(tc_manager_id, id);
	if (db == 0) {
		return 0;
	}
	
	size_t key_len = 0;
	int val_len = 0;
	const char * key = lua_tolstring(L, -1, &key_len);
	char * val = (char*)tchdbget(db->db_ptr, key, key_len, &val_len);
	if (val) {
		lua_pushlstring(L, val, val_len);
		free(val);
		return 1;
	} else {
		return 0;
	}
}

static int tc_out(lua_State * L)
{
	if(!lua_isnumber(L, -2) ) {
		return 0;
	}

	if(!lua_isstring(L, -1) ) {
		return 0;
	}

	int id = lua_tointeger(L, -2);
	struct tc * db = (struct tc *)LRMGetResources(tc_manager_id, id);
	if (db == 0) {
		return 0;
	}
	
	size_t key_len = 0;
	const char * key = lua_tolstring(L, -1, &key_len);
	if (tchdbout(db->db_ptr, key, key_len)) {
		lua_pushinteger(L, 1);
		return 1;
	} else {
		return 0;
	}

}


luaL_Reg tc_functions[] = {
	{"open", tc_open},
	{"close", tc_close},
	{"get", tc_get},
	{"put", tc_put},
	{"out", tc_out},
	{0,0},
};

#ifdef __cplusplus
extern "C" 
#endif 
int registe_tc_function(lua_State * L)
{
	luaL_register(L, "tc", tc_functions);
	return 0;
}
